// use crate::View;
// use crate::UI;
// use super::proxy::{global_brd,brd_once};
use colored::Colorize;
use std::collections::HashMap;
use std::sync::Mutex;
use std::thread;
use std::time::Duration;
use web_view::*;
use std::io::Read;
pub const MSG_SEP: &str = " -|- ";
// pub type R = Receiver<String>;
// pub type S = Sender<String>;
use crossbeam_channel::{bounded, Receiver as MReceiver, Sender as MSender};

pub type S<T> = MSender<T>;
pub type R<T> = MReceiver<T>;
// pub type R = R<String>;
// pub type S = S<String>;
// type SS<T> = Sender<S<T>>;
// type RS<T> = Receiver<S<T>>;

pub const TO_RENDER: &str = "[[TO-RENDER-BRD]]";
pub const TO_HANDLER: &str = "[[TO-HANDLER-BRD]]";
pub const CONSOLE_LOG_MSG: &str = "[[CONSOLE-MSG-FROM-APP]]";
pub struct Brd {
    tx: S<String>,
    rx: R<String>,
    ids: HashMap<String, i32>,
}

impl Brd {
    pub fn regist_brd(&self) -> (S<String>, R<String>) {
        (self.tx.clone(), self.rx.clone())
    }
    pub fn recv_with(rx: R<String>, tx: S<String>, tag: &str) -> Option<String> {
        // let tag = format!("[[{}]]", name);

        // BRD.regist_brd_id(tag);

        loop {
            let m = rx.recv().expect("recv failed");

            if m.starts_with(tag) {
                // println!("r:{}", m.green());
                return Some(m.replace(&tag, ""));
            } else {
                // println!("[{}]r:{}", tag.yellow(),m.blue());
                tx.send(m).expect("try again");
                thread::sleep(Duration::from_secs(1));
            }
        }
    }

    pub fn regist_brd_id(mut self, id: &str) {
        *self.ids.entry(id.to_string()).or_insert(1) += 1;
        // self.ids.insert(id.to_string(), now_num+1);
    }

    // pub fn send_to(tx: S<String>, name: &str, msg: &str) {
    //     // let m = rx.recv().expect("recv failed");
    //     let content = format!("{}{}", name, msg);
    //     tx.send(content).expect("send to failed");
    // }
}

lazy_static! {
    static ref BRD: Brd = {
        let (tx, rx) = bounded::<String>(100);
        Brd {
            tx: tx,
            rx: rx,
            ids: HashMap::new(),
        }
    };
    static ref POOL: Mutex<threadpool::ThreadPool> =
        { Mutex::new(threadpool::ThreadPool::new(10)) };
}

pub fn global_brd() -> (S<String>, R<String>) {
    // let s = BRD.lock().expect("lock failed");
    BRD.regist_brd()
}

pub fn brd(id: &str, msg: &str) {
    // let s = BRD.lock().expect("lock failed");
    let tx = BRD.tx.clone();
    tx.send(format!("{}{}", id, msg))
        .expect("send failed to brd");
}

pub fn brd_regist<F>(id: &str, handle: F)
where
    F: Fn(&str) + Send + 'static,
{
    // let s = BRD.lock().expect("lock failed");
    let r = BRD.rx.clone();
    let t = BRD.tx.clone();
    let i = id.to_string();
    let pool = POOL.lock().expect("no lock pool");

    pool.execute(move || loop {
        let rr = r.clone();
        let tt = t.clone();
        let i_share = i.clone();
        if let Some(msg) = Brd::recv_with(rr, tt, &i_share) {
            let msg_real = msg.replace(&i_share, "");
            handle(&msg_real);
        }
    });
}

pub fn rpc_from_msg(msg: &str) -> (String, String, String) {
    let mut f = msg.split(MSG_SEP);
    (
        f.next().unwrap().to_string(),
        f.next().unwrap().to_string(),
        f.next().unwrap().to_string(),
    )
    // msg.split_twice(view::)
}


pub fn brd_ui_handler<F>(id: &str, handle: F)
where
    F: Fn(&str, &str, &str) + Send + 'static,
{
    brd_regist(&format!("{}{}", TO_HANDLER, id), move |msg| {
        let (i, t, c) = rpc_from_msg(msg);
        handle(&i, &t, &c);
    });
}

pub fn with_build<F>(ui: &UI, invok_handler: F) -> WVResult
where
    F: FnOnce(S<String>, R<String>) + Send + 'static,
{
    let default_css = include_str!("template/default.css");
    let css_boot = include_str!("template/bootstrap-4/css/bootstrap.min.css");
    let js_boot = include_str!("template/bootstrap-4/js/bootstrap.min.js");
    let ie_js = include_str!("template/ie.js");
    let b64_js = include_str!("template/base64.min.js");
    let jquery = include_str!("template/jquery.min.js");
    let default_js = include_str!("template/default.js");
    let hight_css = include_str!("template/light.css");
    let hight_js = include_str!("template/highlight.js");
    let log_js = include_str!("template/log.js");
    let theme = ui.theme;
    let mut body = ui.html.clone();
    let js = ui.js.clone();
    let style = ui.css.clone();
    let size = ui.size;
    let mut theme_css = format!(
        r#"
	background-color: rgb{theme};
    "#,
        theme = &format!("({},{},{})", theme.r, theme.g, theme.b)
    );
    if body.contains("[[THEME]]") {
        body = body.replace("[[THEME]]", &theme_css);
    }
    theme_css = theme_css.replace("++", "{");
    theme_css = theme_css.replace("--", "}");
    let html = format!(
        r#"
<!doctype html>
	<html style = "{theme}">
    <head>
        <style type="text/css" >
        {default_css}
        </style>
        <style type="text/css" >{css}</style>
        <style type="text/css" >{hight}</style>
    </head>
    <body style="{theme}">
        {body}
        
        <script type="text/javascript">
        {log_script}
        </script>
        <!--[if lt IE 9]>
        <script type="text/javascript">{ie_js}</script>
        <![endif]-->
        <!--[if lt IE 8]>
        <script type="text/javascript">{ie_js}</script>
        <![endif]-->
        <!--[if lt IE 7]>
        <script type="text/javascript">{ie_js}</script>
        <![endif]-->
        <script type="text/javascript">
        {jquery}
        </script>
        <script type="text/javascript">{bootstrap}</script>
        <script type="text/javascript">{hight_js}</script>
        <script type="text/javascript">{scripts}</script>
    </body>
</html>
"#,
        log_script=log_js,
        ie_js = ie_js,
        hight = hight_css,
        body = body,
        bootstrap = js_boot,
        jquery = jquery,
        css = &style,
        default_css = &format!("{}\n{}", css_boot, default_css),
        theme = theme_css,
        hight_js = hight_js,
        scripts = &format!("{}\n{}\n{}", b64_js, default_js, &js)
    );

    let pool = POOL.lock().expect("lock thread failed");
    let (s_raw, r_raw) = global_brd();
    let mut webview = web_view::builder()
        .title("example")
        .content(Content::Html(html))
        .size(size.0, size.1)
        .resizable(false)
        .debug(true)
        .user_data(())
        .invoke_handler(|_, arg| {
            let tx = s_raw.clone();
            let value: serde_json::Value = match serde_json::from_str(arg) {
                Ok(a) => a,
                Err(e) => {
                    log::info!("err {}", e.to_string().red());
                    serde_json::from_str(r#"{}"#).expect("no such json")
                }
            };
            if let Some(tp) = value.get("tp") {
                if let Some(from_id) = value.get("id") {
                    if let Some(content) = value.get("content") {
                        let id = from_id.as_str().unwrap().to_string();
                        let msg = content.as_str().unwrap().to_string();
                        let tp = tp.as_str().unwrap().to_string();
                        log::info!(
                            "recv |{}| : {}",
                            from_id.as_str().unwrap().green(),
                            content.as_str().unwrap().yellow()
                        );
                        tx.send(format!("{}{}", TO_HANDLER, vec![id, tp, msg].join(MSG_SEP)))
                            .expect("send failed");
                    }
                }
            } else {
                log::info!(" {:?}", value);
            }
            Ok(())
        })
        .build()
        .unwrap();

    let handle = webview.handle();
    let itx = s_raw.clone();
    let irx = r_raw.clone();
    pool.execute(move || {
        println!("[{}] <--- start job ", "ok".green());
        invok_handler(itx, irx);
    });
    let rx = r_raw.clone();
    let tx = s_raw.clone();
    pool.execute(move || {
        println!("---> start render [{}]", "ok".green());
        loop {
            let rrx = rx.clone();
            let rtx = tx.clone();
            if let Some(msg) = Brd::recv_with(rrx, rtx, TO_RENDER) {
                handle
                    .dispatch(move |web| {
                        println!("---> patch background [{}]", "ok".green());
                        // use base64 to encoding
                        web.render_with_json(&msg);
                        Ok(())
                    })
                    .unwrap();
            }
        }
    });
    webview.set_color(ui.title_color);
    webview.run()
}


pub fn with_build_web<F>(ui: &UI, res_files:Vec<&str>, invok_handler: F) -> WVResult
where
    F: FnOnce(S<String>, R<String>) + Send + 'static,
{
    // let default_css = include_str!("template/default.css");
    let ie_js = include_str!("template/ie.js");
    // let hight_css = include_str!("template/darkula.css");
    let mut pre_css = String::from(r#"<style type="text/css" >"#);
    let mut pre_js = String::from(r#"<script type="text/javascript">"#);
    let mut pre_html = String::new();
    let _ = res_files.iter().map(|p|{
        if std::path::Path::new(p).exists(){
            log::info!("load [{}]: {}","ok".green(), p);
            let mut f = std::fs::File::open(p).unwrap();
            if p.ends_with(".html"){
                f.read_to_string(&mut pre_html).unwrap();
            }else if p.ends_with(".css"){
                f.read_to_string(&mut pre_css).unwrap();
            } else if p.ends_with(".js"){
                f.read_to_string(&mut pre_js).unwrap();
            }
        }else{
            let now_p = std::path::Path::new("./").canonicalize().unwrap();
            log::info!("load [{}]: {} | now in {}","Not Found".red(), p, now_p.to_str().unwrap().yellow());
        }
    }).collect::<Vec<_>>();
    pre_js.push_str("</script>");
    pre_css.push_str("</style>");
    let theme = ui.theme;
    let mut body = ui.html.clone();
    let js = ui.js.clone();
    let style = ui.css.clone();
    let size = ui.size;
    let mut theme_css = format!(
        r#"
	background-color: rgb{theme};
    "#,
        theme = &format!("({},{},{})", theme.r, theme.g, theme.b)
    );
    if body.contains("[[THEME]]") {
        body = body.replace("[[THEME]]", &theme_css);
    }
    theme_css = theme_css.replace("++", "{");
    theme_css = theme_css.replace("--", "}");
    let html = format!(
        r#"
<!doctype html>
	<html style = "{theme}">
    <head>
        {pre_css}
        <style type="text/css" >{css}</style>
    </head>
    <body style="{theme}">
        {pre_html}
        {body}
        <!--[if lt IE 9]>
        <script type="text/javascript">{ie_js}</script>
        <![endif]-->
        <!--[if lt IE 8]>
        <script type="text/javascript">{ie_js}</script>
        <![endif]-->
        <!--[if lt IE 7]>
        <script type="text/javascript">{ie_js}</script>
        <![endif]-->
        {pre_js}
        <script type="text/javascript">{scripts}</script>
    </body>
</html>
"#,
        ie_js = ie_js,
        pre_css=pre_css,
        pre_html=pre_html,
        pre_js=pre_js,
        body = body,
        css = &style,
        theme = theme_css,
        scripts = &js
    );

    let pool = POOL.lock().expect("lock thread failed");
    let (s_raw, r_raw) = global_brd();
    let mut webview = web_view::builder()
        .title("example")
        .content(Content::Html(html))
        .size(size.0, size.1)
        .resizable(true)
        .debug(true)
        .user_data(())
        .invoke_handler(|_, arg| {
            let tx = s_raw.clone();
            let value: serde_json::Value = match serde_json::from_str(arg) {
                Ok(a) => a,
                Err(e) => {
                    log::info!("err {}", e.to_string().red());
                    serde_json::from_str(r#"{}"#).expect("no such json")
                }
            };
            if let Some(tp) = value.get("tp") {
                if let Some(from_id) = value.get("id") {
                    if let Some(content) = value.get("content") {
                        let id = from_id.as_str().unwrap().to_string();
                        let msg = if let Some(msg) = content.as_str(){
                            msg.to_string()
                        }else{
                            "".to_string()
                        };
                        let tp = tp.as_str().unwrap().to_string();
                        // if msg.len()
                        if tp == CONSOLE_LOG_MSG {
                            log::info!(
                                "app msg : {}",
                                msg.green(),
                            );    
                        }else{
                            log::info!(
                                "recv |{}| : {}",
                                from_id.as_str().unwrap().green(),
                                msg.yellow()
                            );
                        }
                        
                        tx.send(format!("{}{}", TO_HANDLER, vec![id, tp, msg].join(MSG_SEP)))
                            .expect("send failed");
                    }
                }
            } else {
                log::info!(" {:?}", value);
            }
            Ok(())
        })
        .build()
        .unwrap();

    let handle = webview.handle();
    let itx = s_raw.clone();
    let irx = r_raw.clone();
    pool.execute(move || {
        println!("[{}] <--- start job ", "ok".green());
        invok_handler(itx, irx);
    });
    let rx = r_raw.clone();
    let tx = s_raw.clone();
    pool.execute(move || {
        println!("---> start render [{}]", "ok".green());
        loop {
            let rrx = rx.clone();
            let rtx = tx.clone();
            if let Some(msg) = Brd::recv_with(rrx, rtx, TO_RENDER) {
                handle
                    .dispatch(move |web| {
                        println!("---> patch background [{}]", "ok".green());
                        // use base64 to encoding
                        web.render_with_json(&msg);
                        Ok(())
                    })
                    .unwrap();
            }
        }
    });
    webview.set_color(ui.title_color);
    webview.run()
}



pub struct UI {
    pub html: String,
    pub css: String,
    pub js: String,
    pub theme: web_view::Color,
    pub size: (i32, i32),
    pub title_color: web_view::Color,
}

impl Default for UI {
    fn default() -> Self {
        Self {
            html: r#""#.to_string(),
            css: r#""#.to_string(),
            size: (360, 600),
            js: "".to_string(),
            theme: web_view::Color::from((5, 44, 92)),
            title_color: web_view::Color::from((5, 44, 92)),
        }
    }
}

impl UI {
    #[allow(unused)]
    fn new(html: &str) -> Self {
        // let h = html_file.read
        Self {
            html: html.to_string(),
            ..Self::default()
        }
    }

    #[allow(dead_code)]
    pub fn set_size(&mut self, w: i32, h: i32) {
        self.size = (w, h);
    }

    #[allow(dead_code)]
    pub fn set_theme(&mut self, r: u8, g: u8, b: u8) {
        self.theme = web_view::Color::from((r, g, b));
    }

    #[allow(dead_code)]
    pub fn add_html(&mut self, new: &str) {
        self.html.push_str("\n");
        self.html.push_str(new);
    }
    #[allow(dead_code)]
    pub fn add_css(&mut self, new: &str) {
        self.css.push_str("\n");
        self.css.push_str(new);
    }
    #[allow(dead_code)]
    pub fn add_js(&mut self, new: &str) {
        self.js.push_str("\n");
        self.js.push_str(new);
    }

    pub fn chain(&mut self, other_ui: &UI) {
        self.add_html(&other_ui.html);
        self.add_css(&other_ui.css);
        self.add_js(&other_ui.js);
    }

    pub fn to_string(&self) -> String {
        let mut m = String::from(format!(
            r#"<style type="text/csstyle">{}</style>"#,
            &self.css
        ));
        m.push_str(&self.html);
        m.push_str(&format!(
            r#"<script type="text/javascript">{}</script>"#,
            &self.js
        ));
        m
    }
}


pub trait View<'a> {
    fn render(&mut self, id: &str, content: &str);
    fn render_with_json(&mut self, json_data: &str);
    fn render_with_list(&mut self, list: &Vec<String>);
}

#[derive(Serialize, Deserialize)]
pub struct Rpc {
    pub content: Vec<String>,
    pub id: String,
    pub tp: String,
    pub progress: String,
}


impl<'a> View<'a> for web_view::WebView<'a, ()> {
    fn render_with_list(&mut self, list: &Vec<String>) {
        let v = Rpc {
            content: list.clone(),
            ..Rpc::default()
        };

        let vv = serde_json::to_string(&v).unwrap();
        self.render_with_json(&vv);
    }

    fn render(&mut self, id: &str, content: &str) {
        let r = Rpc {
            id: id.to_string(),
            content: vec![content.to_string()],
            progress: "none".to_string(),
            tp: "1".to_string(),
        };
        let rpc_str = serde_json::to_string(&r).unwrap();
        let _ = self.eval(&format!("rpc.render('{}')", &rpc_str));
    }

    fn render_with_json(&mut self, json_data: &str) {
        let _ = self.eval(&format!("rpc.render('{}')", json_data));
    }
}

impl Rpc {
    pub fn to_msg(id: &str, tp: &str, content: &str) -> String {
        let r = Rpc {
            id: id.to_string(),
            tp: tp.to_string(),
            progress: "none".to_string(),
            content: vec![content.to_string()],
        };
        serde_json::to_string(&r).unwrap()
    }
}

impl Default for Rpc {
    fn default() -> Self {
        Self {
            id: "send".to_string(),
            tp: "list".to_string(),
            progress: "none".to_string(),
            content: vec![],
        }
    }
}

pub fn app_with<'a, F>(html: &UI, res_files:Vec<&str>, how_handle: F)
where
    F: Fn(S<String>, R<String>) + Send + 'static,
{
    let _ = with_build_web(html, res_files,  how_handle);
}


#[macro_export]
macro_rules! with_html {
    (@e $ui:ident ($tag:expr)) => {
        {
            let html:String = $tag;
            $ui.add_html(&html);
        }
    };
    (@e $ui:ident ($tag:tt $id:tt)) => {
        {
            let html = ele!( ($tag $id) );
            $ui.add_html(&html);
        }
    };
    (@file $UI:ident $base:tt @css $css:tt  @js $($js:tt)* ) => {
        {

            let mut ui = $UI::default();
            let mut buf = include_str!($base);
            let mut jss = String::new();
            let css = include_str!($css);
            $(
                jss.push_str(&include_str!($js) ) ;
            )*

            ui.add_html(&buf);
            ui.add_css(&css);
            ui.add_js(&jss);
            ui
        }
    };
    (@html $UI:ident $(($tag:tt $($id:tt)* )),* @css $($style:tt)* ) =>{
        {
            // use r_ui::view::UI;
            let mut ui = $UI::default();
            $(
                with_html!{@e ui
                    ($tag $($id)* )
                };
            )*

            // ui.add_html(&h);
            let css = stringify!($($style)*);
            if css.contains("@ js"){
                let fs:Vec<&str>  = css.split("@ js").collect();
                let ncss = fs[0].to_string().replace(" ", "");
                let js = fs.last().unwrap();
                ui.add_css(&ncss);
                ui.add_js(&js);
            }else{
                let ncss = css.replace(" ","");
                ui.add_css(&ncss);
            }
            ui
        }
    };

    (@html $UI:ident $(($expr:expr)),* @css $($style:tt)* ) =>{
        {
            // use r_ui::view::UI;
            let mut ui = $UI::default();
            $(
                // let html:String = $expr;
                // ui.add_html(&html);
                with_html!(@e ui ($expr));
            )*
            let css = stringify!($($style)*);
            if css.contains("@ js"){
                let fs:Vec<&str>  = css.split("@ js").collect();
                let ncss = fs[0].to_string().replace(" ", "");
                let js = fs.last().unwrap();
                ui.add_css(&ncss);
                ui.add_js(&js);
            }else{
                let ncss = css.replace(" ","");
                ui.add_css(&ncss);
            }
            ui
        }
    };
    (@li $UI:ident  $(( $e:ident $sid:tt )),*  @css $($style:tt)* ) => {
        {
            // use r_ui::view::UI;
     
            let mut ui = $UI::default();
            let h = ele!(L "list-container" $( ($e $sid  ) ),*  );
            ui.add_html(&h);
            let css = stringify!($($style)*);
            if css.contains("@ js"){
                let fs:Vec<&str>  = css.split("@ js").collect();
                let ncss = fs[0].to_string().replace(" ", "");
                let js = fs.last().unwrap();
                ui.add_css(&ncss);
                ui.add_js(&js);
            }else{
                let ncss = css.replace(" ","");
                ui.add_css(&ncss);
            }


            ui
        }
    };
    (@div $UI:ident $( ($ele:ident $id:tt  ) ),* @css $($style:tt)* ) => {
        {
            
            let mut ui = $UI::default();
            let h = ele!("main" $( ($ele $id  ) ),* );
            ui.add_html(&h);
            let css = stringify!($($style)*);
            if css.contains("@ js"){
                let fs:Vec<&str>  = css.split("@ js").collect();
                let ncss = fs[0].to_string().replace(" ", "");
                let js = fs.last().unwrap();
                ui.add_css(&ncss);
                ui.add_js(&js);
            }else{
                let ncss = css.replace(" ","");
                ui.add_css(&ncss);
            }


            ui
        }
    };
}